/**
 * Copyright 2004 Cordys R&D B.V. 
 * 
 * This file is part of the Cordys FTP Connector. 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 package com.eibus.applicationconnector.ftp;

import java.io.IOException;
import java.net.SocketException;
import javax.xml.namespace.QName;

import com.eibus.applicationconnector.ftp.localization.message.Messages;
import com.eibus.connector.nom.Connector;
import com.eibus.exception.ExceptionGroup;
import com.eibus.localization.IStringResource;
import com.eibus.soap.BodyBlock;
import com.eibus.soap.MethodDefinition;
import com.eibus.soap.Processor;
import com.eibus.soap.fault.Fault;
import com.eibus.soap.fault.FaultDetail;
import com.eibus.xml.nom.Document;
import com.eibus.xml.nom.Node;
import com.eibus.xml.xpath.XPath;
import com.eibus.util.logger.CordysLogger;
import com.eibus.util.logger.Severity;
import com.eibus.util.ConnectorUtils;
import com.eibus.util.Queue;
import com.eibus.directory.soap.DirectoryException;

/**
 * This is an abstract class which is extended by 
 * all the handlers implementing FTP commands 
 */
abstract class FTPCommand
{
	protected BodyBlock response;
	protected int requestXML;
	protected int noOfConnections;
	protected int noOfActiveConnections;
	protected Processor processor;
	protected Queue ftpClients;
	protected String orgContext;
 	protected String ftpNotificationSubject;
 	protected static final int DEFAULT_PORT_NO = 21;
 	protected static final String inProgressMesg = "Transfer In Progress. Check With Event Service For Status Of Transaction.";
    protected static final CordysLogger logger = CordysLogger.getCordysLogger(FTPCommand.class);
    protected static final  String DEFAULT_NAME_SPACE = "http://schemas.cordys.com/1.0/ftp";
    protected static final QName DEFAULT_QNAME = new QName(DEFAULT_NAME_SPACE,"customFaultDetail");
    protected IFTPClient adapter = null;
    protected FTPConfiguration ftpConfiguration = null;
    protected static String METHOD_NAMESPACE = "";
    
	abstract boolean prepare();

	/** Commit of the action to be executed on the object. */
	abstract void commit();

	/**
	 * @return true if successfully connected; false otherwise
	 */
	protected boolean connect()
	{
		/* If the protocol is SFTP, create an object of type CordysSFTPClient */
		if (ftpConfiguration.getProtocol().equals("SFTP"))
		{
			logger.log(Severity.INFO, "Configuration is set to use SFTP protocol.");
			adapter =  new CordysSFTPClient();
		}
		/* Else, if the protocol is FTP, create an object of type CordysFTPClient */
		else
		{
			logger.log(Severity.INFO, "Configuration is set to use normal FTP protocol.");
			adapter =  new CordysFTPClient();
		}
		
		try
		{
			adapter.connect(ftpConfiguration);
		}
        catch(FTPException e)
        {
        	logger.log(Severity.ERROR, e, Messages.FTPSERVER_CONNECTION_FAILED);
            Fault fault = response.createSOAPFault(Fault.Codes.CLIENT, Messages.FTPSERVER_CONNECTION_FAILED);
            addFaultDetails2(fault.getDetail(),e,DEFAULT_QNAME,null,null);
            return false;
        }
		catch (SocketException e)
		{
			logger.log(Severity.ERROR, e, Messages.FTPSERVER_CONNECTION_FAILED);
            Fault fault = response.createSOAPFault(Fault.Codes.CLIENT, Messages.FTPSERVER_CONNECTION_FAILED);
            addFaultDetails2(fault.getDetail(), e, DEFAULT_QNAME, null, null);
            return false;
		}
		catch (IOException e)
		{
			logger.log(Severity.ERROR, e, Messages.FTPSERVER_CONNECTION_FAILED);
            Fault fault = response.createSOAPFault(Fault.Codes.CLIENT, Messages.FTPSERVER_CONNECTION_FAILED);
            addFaultDetails2(fault.getDetail(), e, DEFAULT_QNAME, null, null);
            return false;
		}
		
		return true;
	}
	
	/**
	 * Disconnect the adapter from the FTP server
	 */
	protected void disconnect()
	{
		if (adapter != null)
		{
			adapter.disconnect();
		}
	}
   

	/**
	 * Publish the response to Event Service, if asynchronous mode is selected
	 * 
	 * @param subject
	 * @param peventNode
	 * @throws ExceptionGroup
	 * @throws DirectoryException
	 */
    void publishFTPMessage(String subject, int peventNode) throws ExceptionGroup,DirectoryException
	{
		Connector connector = ConnectorUtils.getConnector();
		int soapMethod = 0;
		try
		{
			String eventSerivceNameSpace ="http://schemas.cordys.com/1.0/eventservice";
			soapMethod = connector.createSOAPMethod(this.orgContext,eventSerivceNameSpace ,"Publish");
			int envelopeNode = Node.getParent(Node.getParent(soapMethod));
			int headerNode = Node.getFirstChildElement(Node.getFirstChildElement(envelopeNode));
			Document document = Node.getDocument(peventNode);
			document.createElementWithParentNS("msg-id",subject,headerNode);
			int subjectNode = document.createElementWithParentNS("subject",subject,soapMethod);
			int eventNode = document.createElement("event");
			Node.appendToChildren(peventNode , eventNode);
			Node.appendToChildren(subjectNode,soapMethod);
			Node.appendToChildren(eventNode,soapMethod);
			connector.send(soapMethod);
		} finally
		{
			Node.delete(Node.getRoot(soapMethod));
			soapMethod = 0;
		}
	}

    protected static  void addFaultDetails2(FaultDetail faultDetail,Throwable exception,QName qname,IStringResource details,Object[] detailInsertionArguments)
    {
    	if(details != null)
    	{
    		faultDetail.addDetailEntry(qname, details, detailInsertionArguments);
    	}

       if(exception != null)
       {
    	   faultDetail.addDetailEntry(exception);
       }
    }

    /**
	 * Return the namespace.
	 * @return
	 */
	protected String getNameSpace()
	{
		MethodDefinition definition = response.getMethodDefinition();
		if(definition != null)
		{
			return definition.getNamespace();
		}
		return DEFAULT_NAME_SPACE;
	}
	
	/**
	 * Copy the contents of request parameters to responseNode
	 * @param requestNode
	 * @param responseNode
	 */
	protected void addRequestDetailsToResponse(int requestNode, int responseNode)
	{
		int configurationNode = XPath.getFirstMatch("configuration", null, requestNode);
		if (configurationNode == 0)
		{
			Node.createElementWithParentNS("server", Node.getDataWithDefault(Node.getElement(requestNode,"server"), ""), responseNode);
			Node.createElementWithParentNS("user", Node.getDataWithDefault(Node.getElement(requestNode,"user"), ""), responseNode);
			Node.createElementWithParentNS("portno", Node.getDataWithDefault(Node.getElement(requestNode,"portno"), ""), responseNode);
			Node.createElementWithParentNS("notification-subject", Node.getDataWithDefault(Node.getElement(requestNode,"notification-subject"), ""), responseNode);
		}
		else
		{
			Node.createElementWithParentNS("configuration", Node.getDataWithDefault(Node.getElement(requestNode,"configuration"), ""), responseNode);
		}
	}

	/**
	 * This method converts all the types of file separators to a common format
	 * 
	 * @param path
	 * @param removeRoot
	 * @return string which has all the File Seperators in the same format  
	 */
	protected static String getProperFileSeparator(String path, boolean removeRoot)
	{
		if (path.length() == 0)
		{
			return path;
		}
		
		path = path.replace('\\', '/');
		
		int index = 0;
		int len = path.length();
		while (index < len && path.charAt(index) == '/')
		{
			index ++;
		}

		if (index > 0)
		{
			path = path.substring(index-1);			
		}			

		if ( removeRoot && path.charAt(0) == '/' )
		{
			path = path.substring(1);
		}
		return path;
	}
	
	/**
	 * This method validates if the given path contains few characters
	 * like ., .., or /
	 * @param relativePath
	 * @return true/false
	 */
    protected boolean validate(String relativePath)
    {
    	String[] folders = relativePath.split("/");
    	
    	if (relativePath.contains(":"))
    	{
    		return false;
    	}
    	for (int index = 0; index < folders.length; index ++)
    	{
    		if (folders[index].equals(".") || folders[index].equals(".."))
    		{
    			return false;
    		}
    	}
    	return true;
	}
    
	public BodyBlock getResponse() {
		return response;
	}

	public void setResponse(BodyBlock response) {
		this.response = response;
	}

	public int getRequestXML() {
		return requestXML;
	}

	public void setRequestXML(int requestXML) {
		this.requestXML = requestXML;
	}

	public Processor getProcessor() {
		return processor;
	}

	public void setProcessor(Processor processor) {
		this.processor = processor;
	}

	public String getOrgContext() {
		return orgContext;
	}

	public void setOrgContext(String orgContext) {
		this.orgContext = orgContext;
	}

	public FTPConfiguration getFtpConfiguration() {
		return ftpConfiguration;
	}

	public void setFtpConfiguration(FTPConfiguration ftpConfiguration) {
		this.ftpConfiguration = ftpConfiguration;
	}

	public String getFtpNotificationSubject() {
		return ftpNotificationSubject;
	}

	public void setFtpNotificationSubject(String ftpNotificationSubject) {
		this.ftpNotificationSubject = ftpNotificationSubject;
	}
}